import type { App } from 'vue'
import { nextTick } from 'vue'
import utils from '@/js/utils'
import config from '@/config/calculation_config'
import vuex from '@/store'
interface CalculationCache {
	[key: string]: {
		[key: string]: number | string | {}
	}
}
interface ItemUnit {
	ratio: number
}
interface ItemDetailItem {
	assistUnit: string
	assistUnitRatio: number
	canWeigh: number
}
interface ItemDetail<T = number> {
	[key: string]: T
}


const calculationCache: CalculationCache = {}
const parseFixed = function (n: number): number {
	return utils.toFixedParseNumber(n, vuex.getters.getSysMoneyPointSize)
}
const priceFixed = function (n: number): number {
	return utils.toFixedParseNumber(n, vuex.getters.getSysPricePointSize)
}
const amountFixed = function (n: number): number {
	return utils.toFixedParseNumber(n, vuex.getters.getSysPointSize)
}
// 总价运算 = 数量 * 单价
const compSumPrice = function (a: number, p: number): number {
	return a * p
}
// 非税金额计算 = 含税金额 - 税额
const compFinancialSumPrice = function (tm: number, t: number): number {
	return parseFixed(tm - t)
}
// 税额运算 = 含税金额 - 非税金额
const compTaxes = function (tm: number, m: number): number {
	return parseFixed(tm - m)
}
// 含税运算 = 价格 * (1 + 税率)
const compWithTaxes = function (p: number, t: number): number {
	return p * (1 + translatTax(t))
}
// 除税运算 = 金额 / (1 + 税率)
const compNoTaxes = function (tp: number, t: number): number {
	return tp / (1 + translatTax(t))
}
// 单价运算 = 金额 / 数量
const compPrice = function (p: number, a: number): number {
	return priceFixed(p / a)
}
// 单换换算系数 = 新单位换算率 / 旧单位换算率
const compUnitRate = function (na: number, oa: number): number {
	return na / oa
}
// 单位换算价格 = 原价格 * 单换换算系数
const compPriceByUnitRate = function (p: number, u: number): number {
	return priceFixed(p * u)
}
// 单位换算数量 = 原数量 / 单换换算系数
const compAmountByUnitRate = function (p: number, u: number): number {
	return amountFixed(p / u)
}
// 税率换算 : 云供应链 税率 均为整数 z5税率 均为小数 因此计算前转成小数
const translatTax = function (tax: number): number {
	return tax < 1 ? tax : tax / 100
}
interface CalculateParams {
	amount: string
	price: string
	sumPrice: string
	priceWithTax: string
	sumPriceWithTax: string
	taxes: string
	taxMoney: string
	assistUnitAmount: string
	mergeBatchPadMinItemUnitAmount: string
}
class CopmutedFunc {
	private amount: string
	private price: string
	private sumPrice: string
	private priceWithTax: string
	private sumPriceWithTax: string
	private taxes: string
	private taxMoney: string
	private assistUnitAmount: string
	private mergeBatchPadMinItemUnitAmount: string
	[key: string]: Function | string | WeakMap<object, object>
	calculationCache: WeakMap<object, object>
	constructor ({
		// 数量
		amount,
		// 非税单价
		price,
		// 非税金额
		sumPrice,
		// 含税单价
		priceWithTax,
		// 含税金额
		sumPriceWithTax,
		// 税率
		taxes,
		// 税额
		taxMoney,
		// 辅助数量
		assistUnitAmount,
		// 合并批次最小数量
		mergeBatchPadMinItemUnitAmount
	}: CalculateParams) {
		this.amount = amount
		this.price = price
		this.sumPrice = sumPrice
		this.priceWithTax = priceWithTax
		this.sumPriceWithTax = sumPriceWithTax
		this.taxes = taxes
		this.taxMoney = taxMoney
		this.assistUnitAmount = assistUnitAmount
		this.mergeBatchPadMinItemUnitAmount = mergeBatchPadMinItemUnitAmount
		this.calculationCache = new WeakMap()
	}

	// 初始化明细对象
	initDetail<T extends Object> (item: T): void {
		this.calculationCache.set(item, utils.deepClone(item))
	}

	getDetailCache<T extends Object> (item: T): T {
		if (this.calculationCache.has(item)) {
			return this.calculationCache.get(item) as T
		}
		const fxCalculationKey = `${(item as unknown as { $fxCalculation: string }).$fxCalculation}`
		return (calculationCache[fxCalculationKey] || {}) as unknown as T
	}

	// 修改数量
	changeAmount (item: ItemDetail): void {
		const cacheItemDetail = this.getDetailCache(item)
		if (
			`${cacheItemDetail[this.amount]}` === `${item[this.amount]}` &&
			`${cacheItemDetail[this.price]}` === `${item[this.price]}` &&
			`${cacheItemDetail[this.priceWithTax]}` === `${item[this.priceWithTax]}`
		) {
			item[this.sumPrice] = cacheItemDetail[this.sumPrice] as number
			item[this.sumPriceWithTax] = cacheItemDetail[this.sumPriceWithTax] as number
			item[this.taxMoney] = cacheItemDetail[this.taxMoney] as number
		} else {
			item[this.sumPrice] = parseFixed(compSumPrice(item[this.amount], item[this.price]))
			if (item.moneyChange) {
				item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.amount])
			} else {
				item[this.sumPriceWithTax] = parseFixed(compSumPrice(item[this.amount], item[this.priceWithTax]))
			}
			item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
		}
	}

	// 入库单合并批次修改数量
	changeAmountBatch (item: ItemDetail): void {
		const cacheItemDetail = this.getDetailCache(item)
		if (
			`${cacheItemDetail[this.mergeBatchPadMinItemUnitAmount]}` === `${item[this.mergeBatchPadMinItemUnitAmount]}` &&
			`${cacheItemDetail[this.price]}` === `${item[this.price]}` &&
			`${cacheItemDetail[this.priceWithTax]}` === `${item[this.priceWithTax]}`
		) {
			item[this.sumPrice] = cacheItemDetail[this.sumPrice] as number
			item[this.sumPriceWithTax] = cacheItemDetail[this.sumPriceWithTax] as number
			item[this.taxMoney] = cacheItemDetail[this.taxMoney] as number
		} else {
			item[this.sumPrice] = parseFixed(compSumPrice(item[this.mergeBatchPadMinItemUnitAmount], item[this.price]))
			if (item.moneyChange) {
				item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.mergeBatchPadMinItemUnitAmount])
			} else {
				item[this.sumPriceWithTax] = parseFixed(compSumPrice(item[this.mergeBatchPadMinItemUnitAmount], item[this.priceWithTax]))
			}
			item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
		}
	}

	// 修改非税单价
	changePrice (item: ItemDetail): void {
		const _sumPrice = compSumPrice(item[this.amount], item[this.price])
		const _sumPriceWithTax = compWithTaxes(_sumPrice, item[this.taxes])
		item[this.sumPrice] = parseFixed(_sumPrice)
		item[this.sumPriceWithTax] = parseFixed(_sumPriceWithTax)
		if (item[this.amount] === 0) {
			item[this.priceWithTax] = priceFixed(compWithTaxes(item[this.price], item[this.taxes]))
		} else {
			item[this.priceWithTax] = compPrice(_sumPriceWithTax, item[this.amount])
		}
		item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
	}

	// 修改非税金额
	changeSumPrice (item: ItemDetail): void {
		if (item[this.amount] === 0) {
			nextTick(() => {
				item[this.sumPrice] = 0
			})
		} else {
			item[this.price] = compPrice(item[this.sumPrice], item[this.amount])
			item[this.sumPriceWithTax] = parseFixed(compWithTaxes(item[this.sumPrice], item[this.taxes]))
			item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.amount])
			item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
		}
	}

	// 修改含税单价
	changePriceWithTax (item: ItemDetail): void {
		const _sumPriceWithTax = compSumPrice(item[this.amount], item[this.priceWithTax])
		const _sumPrice = compNoTaxes(_sumPriceWithTax, item[this.taxes])
		item[this.sumPriceWithTax] = parseFixed(_sumPriceWithTax)
		item[this.sumPrice] = parseFixed(_sumPrice)
		if (item[this.amount] === 0) {
			item[this.price] = priceFixed(compNoTaxes(item[this.priceWithTax], item[this.taxes]))
		} else {
			item[this.price] = compPrice(_sumPrice, item[this.amount])
		}
		item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
	}

	// 入库单合批修改含税单价
	changePriceWithTaxBatch (item: ItemDetail): void {
		const _sumPriceWithTax = compSumPrice(item[this.mergeBatchPadMinItemUnitAmount], item[this.priceWithTax])
		const _sumPrice = compNoTaxes(_sumPriceWithTax, item[this.taxes])
		item[this.sumPriceWithTax] = parseFixed(_sumPriceWithTax)
		item[this.sumPrice] = parseFixed(_sumPrice)
		if (item[this.mergeBatchPadMinItemUnitAmount] === 0) {
			item[this.price] = priceFixed(compNoTaxes(item[this.priceWithTax], item[this.taxes]))
		} else {
			item[this.price] = compPrice(_sumPrice, item[this.mergeBatchPadMinItemUnitAmount])
		}
		item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
	}

	// 修改含税金额
	changeSumPriceWithTax (item: ItemDetail): void {
		if (item[this.amount] === 0) {
			nextTick(() => {
				item[this.priceWithTax] = 0
			})
		} else {
			item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.amount])
			item[this.sumPrice] = parseFixed(compNoTaxes(item[this.sumPriceWithTax], item[this.taxes]))
			item[this.price] = compPrice(item[this.sumPrice], item[this.amount])
			item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
		}
	}

	// 入库单合批修改含税金额
	changeSumPriceWithTaxBatch (item: ItemDetail): void {
		if (item[this.mergeBatchPadMinItemUnitAmount] === 0) {
			nextTick(() => {
				item[this.priceWithTax] = 0
			})
		} else {
			item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.mergeBatchPadMinItemUnitAmount])
			item[this.sumPrice] = parseFixed(compNoTaxes(item[this.sumPriceWithTax], item[this.taxes]))
			item[this.price] = compPrice(item[this.sumPrice], item[this.mergeBatchPadMinItemUnitAmount])
			item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
		}
	}

	// 修改含税金额(财务模块使用)
	changeFinancialSumPriceWithTax (item: ItemDetail): void {
		item[this.sumPrice] = parseFixed(compFinancialSumPrice(item[this.sumPriceWithTax], item[this.taxMoney]))
		if (item[this.amount] !== 0) {
			item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.amount])
			item[this.price] = compPrice(item[this.sumPrice], item[this.amount])
		}
	}

	// 修改税额(财务模块使用)
	changeFinancialTaxMoney (item: ItemDetail): void {
		item[this.sumPrice] = parseFixed(compFinancialSumPrice(item[this.sumPriceWithTax], item[this.taxMoney]))
		if (item[this.amount] !== 0) {
			item[this.priceWithTax] = compPrice(item[this.sumPriceWithTax], item[this.amount])
			item[this.price] = compPrice(item[this.sumPrice], item[this.amount])
		}
	}

	// 修改税率
	changeTaxes (item: ItemDetail): void {
		const _sumPrice = compNoTaxes(item[this.sumPriceWithTax], item[this.taxes])
		item[this.sumPrice] = parseFixed(_sumPrice)
		if (item[this.amount] === 0) {
			item[this.price] = priceFixed(compNoTaxes(item[this.priceWithTax], item[this.taxes]))
		} else {
			item[this.price] = compPrice(_sumPrice, item[this.amount])
		}
		item[this.taxMoney] = compTaxes(item[this.sumPriceWithTax], item[this.sumPrice])
	}

	// 修改单位
	changeUnit (item: ItemDetail, unit: ItemUnit, oldUnitRatio = 0) {
		const unitRate = compUnitRate(unit.ratio, oldUnitRatio || (item as unknown as ItemDetail<ItemUnit>).itemUnit.ratio)
		// 参与称重的品项切换单位，数量需要进行换算
		if ((item as unknown as ItemDetail<ItemDetailItem>).item.canWeigh) {
			item[this.amount] = compAmountByUnitRate(item[this.amount], unitRate)
		}
		if (utils.isDef(item.sellerPrice)) {
			item.sellerPrice = compPriceByUnitRate(item.sellerPrice, unitRate)
			item.sellerMoney = parseFixed(compSumPrice(item[this.amount], item.sellerPrice))
		}
		item[this.priceWithTax] = compPriceByUnitRate(item[this.priceWithTax], oldUnitRatio ? 1 : unitRate)
		this.changePriceWithTax(item);
		(item as unknown as ItemDetail<ItemUnit>).itemUnit = unit
		this.calculateAssistAmount(item, (item as unknown as ItemDetail<ItemUnit[]>).itemUnitList)
	}

	// 修改单位（不重新计算辅助单位数量）
	changeUnitNotCalculateAssistAmount (item: ItemDetail, unit: ItemUnit) {
		const unitRate = compUnitRate(unit.ratio, (item as unknown as ItemDetail<ItemUnit>).itemUnit.ratio)
		// 参与称重的品项切换单位，数量需要进行换算
		if ((item as unknown as ItemDetail<ItemDetailItem>).item.canWeigh) {
			item[this.amount] = compAmountByUnitRate(item[this.amount], unitRate)
		}
		item[this.priceWithTax] = compPriceByUnitRate(item[this.priceWithTax], unitRate)
		this.changePriceWithTax(item);
		(item as unknown as ItemDetail<ItemUnit>).itemUnit = unit
	}

	// 计算辅助数量
	calculateAssistAmount (item: ItemDetail, unitList: ItemUnit[]) {
		if ((item as unknown as ItemDetail<ItemDetailItem>).item.assistUnit) {
			let mainUnit = (item as unknown as ItemDetail<ItemUnit>).itemUnit
			if (!mainUnit) {
				if (!(Array.isArray(unitList) && unitList.length > 0)) {
					(item as unknown as ItemDetail<string>)[this.assistUnitAmount] = ''
					return false
				}
				mainUnit = unitList[0]
			}
			item[this.assistUnitAmount] = amountFixed(mainUnit.ratio / (item as unknown as ItemDetail<ItemDetailItem>).item.assistUnitRatio * item[this.amount])
		} else {
			(item as unknown as ItemDetail<string>)[this.assistUnitAmount] = ''
		}
	}

	// 入库合批计算辅助数量
	calculateAssistMergeBatchPadMinItemUnitAmount (item: ItemDetail, unitList: ItemUnit[]) {
		if ((item as unknown as ItemDetail<ItemDetailItem>).item.assistUnit) {
			let mainUnit = (item as unknown as ItemDetail<ItemUnit>).itemUnit
			if (!mainUnit) {
				if (!(Array.isArray(unitList) && unitList.length > 0)) {
					(item as unknown as ItemDetail<string>)[this.assistUnitAmount] = ''
					return false
				}
				mainUnit = unitList[0]
			}
			item[this.assistUnitAmount] = amountFixed(mainUnit.ratio / (item as unknown as ItemDetail<ItemDetailItem>).item.assistUnitRatio * item[this.mergeBatchPadMinItemUnitAmount])
		} else {
			(item as unknown as ItemDetail<string>)[this.assistUnitAmount] = ''
		}
	}

	// 赠品设置
	calculateGift (item: ItemDetail) {
		if (item.giftFlag) {
			item[this.price] = 0
			item[this.taxes] = 0
			this.changePrice(item)
		}
	}

	// 业务单位与辅助单位一致时，修改辅助数量
	changeAssistAmount (item: ItemDetail) {
		item[this.amount] = item[this.assistUnitAmount]
		this.changeAmount(item)
	}

	// 修改单位-计算订货方案里的上下限
	changeUnitCompSupperLimitOrLowerLimit (item: { $fxCalculation: string, itemUnit: { id?: string, ratio: number }, stockPlanDetail: { lowerLimit: number, supperLimit: number } }, unit: { id: '', ratio: number }) {
		const unitRate = unit.ratio / item.itemUnit.ratio
		const cacheItemDetail = this.getDetailCache(item)
		if (`${cacheItemDetail.itemUnit.id}` === `${unit.id}`) {
			// 修改下限
			item.stockPlanDetail.lowerLimit = cacheItemDetail.stockPlanDetail.lowerLimit
			// 修改上限
			item.stockPlanDetail.supperLimit = cacheItemDetail.stockPlanDetail.supperLimit
		} else {
			// 修改下限
			item.stockPlanDetail.lowerLimit = amountFixed(item.stockPlanDetail.lowerLimit / unitRate)
			// 修改上限
			item.stockPlanDetail.supperLimit = amountFixed(item.stockPlanDetail.supperLimit / unitRate)
		}
	}
}

export interface FxCalculation {
	(billType: string): Record<string, Function>
	setCache (row: {
		$fxCalculation?: string;
	}): void
}

const fxCalculation = function (billType: string) {
	const billConfig = config[billType]
	const computedFunc = new CopmutedFunc(billConfig.init as CalculateParams)
	Object.keys(billConfig).forEach(item => {
		computedFunc[item] = <Function>billConfig[item]
	})
	return computedFunc
}
fxCalculation.setCache = function (row: { $fxCalculation: string }) {
	if (!calculationCache[row.$fxCalculation]) {
		calculationCache[row.$fxCalculation] = utils.deepClone(row)
	}
}
const install = (app: App): void => {
	app.config.globalProperties.$fxCalculation = fxCalculation
}

export type CopmutedFuncType = CopmutedFunc

export default {
	install
}

